﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XAsset;

namespace TPF.ObjectCache
{
    public sealed class PoolRequest : IEnumerator
    {
        #region IEnumerator implementation

        public bool MoveNext()
        {
            return !IsDone;
        }

        public void Reset()
        {
        }

        public object Current
        {
            get { return null; }
        }

        #endregion

        private readonly Asset _asset;
        // 控制对象实例化的队列引用，当异步加载模板资源完成时，PoolRequest对象并不会直接将模板资源实例化并通知外界，
        // 主要原因是考虑到某些情况同一帧回调次数太多会导致卡顿，所以把实例化对象行为的调度放到PoolCore中
        // _objectInstantiateControllerQueue即是引用了PoolCore中的成员
        private readonly Queue<ObjectInstantiateMethod> _objectInstantiateControllerQueue;
        private bool _isCanceled; // 是否取消
        private Action<GameObject> _completed;
        private ObjectInstantiateMethod _objectInstantiateMethod;

        public GameObject GameObject { get; private set; }

        /// <summary>
        /// 异步加载是否完成
        /// </summary>
        public bool IsDone
        {
            get { return GameObject != null; }
        }

        /// <summary>
        /// 异步加载的进度
        /// </summary>
        public float Progress
        {
            get
            {
                if (IsDone)
                {
                    return 1f;
                }

                if (_asset == null)
                {
                    return 0;
                }

                return _asset.progress;
            }
        }

        /// <summary>
        /// 设置异步加载完成时的响应
        /// 如果调用方法时加载已完成，会直接响应设置的回调方法
        /// </summary>
        /// <param name="completeCallback"></param>
        public void Complete(Action<GameObject> completeCallback)
        {
            if (IsDone) // 加载已完成
            {
                if (completeCallback != null)
                {
                    completeCallback(GameObject);
                }
            }
            else
            {
                _completed = completeCallback;
            }
        }

        /// <summary>
        /// 取消加载，加载未完成时调用了该方法将收不到Request的回调；如果加载已完成，该方法没有任何作用
        /// </summary>
        public void Cancel()
        {
            if (!IsDone)
            {
                _isCanceled = true;

                // 如果模板资源已加载完成，将ObjectInstantiateMethod也标记为Cancel
                if (_objectInstantiateMethod != null)
                {
                    _objectInstantiateMethod.IsCanceled = true;
                }
            }
        }

        internal PoolRequest(Asset asset, Queue<ObjectInstantiateMethod> objectControllerQueue)
        {
            if (asset != null)
            {
                _asset = asset;
                _asset.completed += OnAssetLoadComplete;

                _objectInstantiateControllerQueue = objectControllerQueue;
            }
        }

        internal PoolRequest(GameObject gameObject)
        {
            GameObject = gameObject;
        }

        /// <summary>
        /// 对象模板资源加载完成时的回调
        /// </summary>
        /// <param name="asset"></param>
        private void OnAssetLoadComplete(Asset asset)
        {
            if (_isCanceled)
            {
                return;
            }

            _objectInstantiateMethod = new ObjectInstantiateMethod(asset, OnObjectInstantiated);
            _objectInstantiateControllerQueue.Enqueue(_objectInstantiateMethod);
        }

        /// <summary>
        /// 对象实例化完成时的回调，由PoolCore发起调用
        /// </summary>
        /// <param name="go"></param>
        private void OnObjectInstantiated(GameObject go)
        {
            GameObject = go;
            if (_completed != null)
            {
                _completed(GameObject);
            }
        }
    }

    internal class ObjectInstantiateMethod
    {
        public Asset Asset { get; private set; }
        public Action<GameObject> OnObjectInstantiated { get; private set; }
        public bool IsCanceled { get; set; }

        public ObjectInstantiateMethod(Asset asset, Action<GameObject> onObjectInstantiated)
        {
            Asset = asset;
            OnObjectInstantiated = onObjectInstantiated;
        }
    }
}